package 加密与安全;

/*
我们使用非对称加密算法的时候，对于一个公钥-私钥对，通常是用公钥加密，私钥解密。

如果使用私钥加密，公钥解密是否可行呢？实际上是完全可行的。

不过我们再仔细想一想，私钥是保密的，而公钥是公开的，用私钥加密，那相当于所有人都可以用公钥解密。这个加密有什么意义？

这个加密的意义在于，如果小明用自己的私钥加密了一条消息，比如小明喜欢小红，然后他公开了加密消息，由于任何人都可以用小明的公钥解密，从而使得任何人都可以确认小明喜欢小红这条
消息肯定是小明发出的，其他人不能伪造这个消息，小明也不能抵赖这条消息不是自己写的。

因此，私钥加密得到的密文实际上就是数字签名，要验证这个签名是否正确，只能用私钥持有者的公钥进行解密验证。使用数字签名的目的是为了确认某个信息确实是由某个发送方发送的，任何
人都不可能伪造消息，并且，发送方也不能抵赖。

在实际应用的时候，签名实际上并不是针对原始消息，而是针对原始消息的哈希进行签名，即：
signature = encrypt(privateKey, sha256(message))

对签名进行验证实际上就是用公钥解密：
hash = decrypt(publicKey, signature)

然后把解密后的哈希与原始消息的哈希进行对比。

因为用户总是使用自己的私钥进行签名，所以，私钥就相当于用户身份。而公钥用来给外部验证用户身份。

常用数字签名算法有：

MD5withRSA
SHA1withRSA
SHA256withRSA

它们实际上就是指定某种哈希算法进行RSA签名的方式。
*/
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

/*
public class 签名算法 {
    public static void main(String[] args) throws GeneralSecurityException {
        // 生成RSA公钥/私钥:
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
        
        //KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DSA");
        //DSA一般用于数字签名和认证，在DSA数字签名和认证中，发送者使用自己的私钥对文件或消息进行签名，接受者收到消息后使用发送者的公钥来验证签名的真实性。DSA只是一种
		  //算法，和RSA不同之处在于它不能用作加密和解密，也不能进行密钥交换，只用于签名,它比RSA要快很多.
		  
        kpGen.initialize(1024);
        KeyPair kp = kpGen.generateKeyPair();
        PrivateKey sk = kp.getPrivate();
        PublicKey pk = kp.getPublic();

        // 待签名的消息:
        byte[] message = "Hello, I am Bob!".getBytes(StandardCharsets.UTF_8);//默认为UTF8

        // 用私钥签名:
        Signature s = Signature.getInstance("SHA1withRSA");
        //Signature s = Signature.getInstance("SHA1withDSA");
        s.initSign(sk);
        s.update(message);
        byte[] signed = s.sign();
        System.out.println(String.format("signature: %x", new BigInteger(1, signed)));

        // 用公钥验证:
        Signature v = Signature.getInstance("SHA1withRSA");
        //Signature v = Signature.getInstance("SHA1withDSA");
        v.initVerify(pk);
        v.update(message);
        boolean valid = v.verify(signed);
        System.out.println("valid? " + valid);
    }
}
*/
/*
使用其他公钥，或者验证签名的时候修改原始信息，都无法验证成功。

DSA签名
除了RSA可以签名外，还可以使用DSA算法进行签名。DSA是Digital Signature Algorithm的缩写，它使用ElGamal数字签名算法。

DSA一般用于数字签名和认证，在DSA数字签名和认证中，发送者使用自己的私钥对文件或消息进行签名，接受者收到消息后使用发送者的公钥来验证签名的真实性。DSA只是一种
算法，和RSA不同之处在于它不能用作加密和解密，也不能进行密钥交换，只用于签名,它比RSA要快很多.

DSA只能配合SHA使用，常用的算法有：

SHA1withDSA
SHA256withDSA
SHA512withDSA
和RSA数字签名相比，DSA的优点是更快。
*/

public class 签名算法 {
	public static void main(String[] args) throws Exception {
		// 在使用BouncyCastle前，首先需要将BouncyCastle加入到Security的提供者中：
		// 虚拟机会使用Java自带的默认提供者进行签名等相关操作，但是自带的提供者库是不支持很多新的算法和曲线的。
		BouncyCastleProvider provider = new BouncyCastleProvider();
		// 选择Provider
		Security.addProvider(provider);

	   // Security.addProvider(new BouncyCastleProvider());

		// 生成公私密钥对
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA", provider);
		//KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");//, provider);
		keyPairGenerator.initialize(1024);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		PrivateKey privateKey = keyPair.getPrivate();
		PublicKey publicKey = keyPair.getPublic();

		// 使用私钥进行签名操作
		Signature signature = Signature.getInstance("DSA", provider);
		//Signature signature = Signature.getInstance("DSA");//, provider);
		signature.initSign(privateKey);

		String msg = "Hello, World!";
		signature.update(msg.getBytes("UTF-8"));

		byte[] sigBytes = signature.sign();
		System.out.println(String.format("signature: %x", new BigInteger(1, sigBytes)));
		
		// 使用公钥进行验签操作
		Signature verifier = Signature.getInstance("DSA", provider);
		//Signature verifier = Signature.getInstance("DSA");//, provider);
		verifier.initVerify(publicKey);

		verifier.update(msg.getBytes("UTF-8"));

		boolean isVerified = verifier.verify(sigBytes);
		System.out.println("Signature verification result: " + isVerified);
	}
}

/*
ECDSA签名
椭圆曲线签名算法ECDSA：Elliptic Curve Digital Signature Algorithm也是一种常用的签名算法，它的特点是可以从私钥推出公
钥。比特币的签名算法就采用了ECDSA算法，使用标准椭圆曲线secp256k1。BouncyCastle提供了ECDSA的完整实现。
*/